package edu.uoc.vmerinom.tfc.support;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import net.sf.jooreports.templates.DocumentTemplate;
import net.sf.jooreports.templates.DocumentTemplateException;
import net.sf.jooreports.templates.DocumentTemplateFactory;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.ServletLifecycle;
import org.jboss.seam.log.Log;

import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry;
import com.artofsolving.jodconverter.DocumentConverter;
import com.artofsolving.jodconverter.DocumentFormat;
import com.artofsolving.jodconverter.DocumentFormatRegistry;
import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;

/**
 * Clase de soporte con métodos para las operaciones habituales con
 * documentos.
 * 
 * @author victormerino
 */
@Name("reportSupport")
@Scope(ScopeType.APPLICATION)
public class Report {

	/**
	 * Log para la trazabilidad de errores.
	 */
	@Logger
	static Log log;

	/**
	 * Mapa de configuración de LibreOffice.
	 */
	static Map<String, String> ooconfig;

	static {
		ooconfig = Utils.getProperties("libreoffice");
	}

	/**
	 * Escribe en la salida el array de bytes que recibe por parámetros.
	 * 
	 * @param content
	 *            Array de bytes con el documento
	 * @param fileName
	 *            Nombre del fichero
	 * @param mimeType
	 *            Tipo Mime
	 * @throws IOException
	 *             Excepciones de entrada/salida
	 */
	public static void write(byte[] content, String fileName, String mimeType) throws IOException {
		final FacesContext context = FacesContext.getCurrentInstance();

		if ((content != null) && (content.length > 0)) {
			final HttpServletResponse response = (HttpServletResponse) context.getExternalContext().getResponse();
			response.setContentType(mimeType);
			response.setContentLength(content.length);
			response.setHeader("Content-disposition", "attachment;filename=\"" + fileName + "\"");

			try {
				final ServletOutputStream out = response.getOutputStream();
				out.write(content);
				out.flush();
				out.close();
			} catch (IOException e) {
				log.error("Se ha producido un error al escribir en la salida");
				throw e;
			}
		}
		context.responseComplete();
	}

	/**
	 * Obtiene el documento final con las variables sustituidas a partir de la
	 * plantilla y el modelo que recibe por parámetros.
	 * 
	 * @param templatePath
	 *            Ruta y nombre de la plantilla
	 * @param mapModel
	 *            Mapa modelo con las variables a sustituir en el documento
	 * @return Vector de bytes que representan el documento
	 * @throws IOException
	 *             Excepciones de entrada/salida
	 * @throws DocumentTemplateException
	 *             Excepciones producidas en la sustitución de variables o la
	 *             obtención del documento
	 */
	public static byte[] getDocument(String templatePath, Map<String, Object> mapModel) throws IOException, DocumentTemplateException {
		final String template = ServletLifecycle.getServletContext().getRealPath(templatePath);
		final DocumentTemplateFactory documentTemplateFactory = new DocumentTemplateFactory();
		ByteArrayOutputStream output = null;
		try {
			DocumentTemplate documentTemplate = documentTemplateFactory.getTemplate(new File(template));
			output = new ByteArrayOutputStream();
			documentTemplate.createDocument(mapModel, output);
		} catch (IOException e) {
			log.error("Se ha producido un error de entrada/salida: ", e);
			throw e;
		} catch (DocumentTemplateException e) {
			log.error("Se ha producido un error al sustituir las variables: ", e);
			throw e;
		}
		return output.toByteArray();
	}

	/**
	 * Convierte el documento que recibe por parámetros según los formatos de
	 * entrada y salida indicados con las extensiones que recibe por parámetros.
	 * 
	 * @param document
	 *            Documento a convertir
	 * @param inExt
	 *            Extensión de entrada
	 * @param outExt
	 *            Extensión de salida
	 * @return Documento convertido
	 * @throws ConnectException
	 *             Excepciones producidas por el servidor de LibreOffice
	 */
	public static byte[] convert(byte[] document, String inExt, String outExt) throws ConnectException {
		final String oohost = ooconfig.get("libreoffice.host");
		final int ooport = Integer.valueOf(ooconfig.get("libreoffice.port"));
		final OpenOfficeConnection connection = new SocketOpenOfficeConnection(oohost, ooport);

		try {
			connection.connect();
		} catch (ConnectException e) {
			log.error("Se ha producido un error al intentar conectar con el servidor LibreOffice: ", e);
			throw e;
		}

		final InputStream input = new ByteArrayInputStream(document);
		final ByteArrayOutputStream output = new ByteArrayOutputStream();
		final DocumentConverter converter = new StreamOpenOfficeDocumentConverter(connection);
		final DocumentFormatRegistry registry = new DefaultDocumentFormatRegistry();
		final DocumentFormat formatin = registry.getFormatByFileExtension(inExt);
		final DocumentFormat formatout = registry.getFormatByFileExtension(outExt);

		converter.convert(input, formatin, output, formatout);
		connection.disconnect();

		return output.toByteArray();
	}
}
